package com.itheima.controller;

import com.itheima.pojo.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * postman测试请求参数接收
 *

 * 原始方式获取请求参数:
 * Controller方法形参中声明HttpServletRequest对象
 * 调用对象的getParameter(参数名)
 *
 * SpringBoot中接收简单参数:
 * 请求参数名与方法形参变量名相同
 * 会自动进行类型转换
 *
 * @RequestParam注解:
 * 方法形参名称与请求参数名称不匹配，通过该注解完成映射
 * 该注解的required属性默认是true，代表请求参数必须传递
 *
 * 在web应用程序的请求响应的整个流程中，需要重点关注的是controller程序，controller程序的核心职责就是接受请求，然后设置响应
 * 以下就是关于请求参数的接收：
 *
 * 说明：@RestController = @Controller + @ResponseBody ;
 *      可以点@RestController注解看看，封装了三个元注解，还封装了@Controller 和 @ResponseBody注解
 *
 */

@RestController     //类上加了该注解后，就代表当前类下的所有方法的返回值都会作为响应数据
//@Controller
//@ResponseBody
public class RequestController {

     //1. 简单参数
     //原始方式的写法，在原始的web程序中，获取请求参数，需要通过HttpServletRequest 对象手动获取。
//    @RequestMapping("/simpleParam")
//    public String simpleParam(HttpServletRequest request){
//        //获取请求参数
//        String name = request.getParameter("name");
//        String ageStr = request.getParameter("age");
//
//        int age = Integer.parseInt(ageStr);       //类型转换，非常繁琐
//        System.out.println(name+ ":" + age);
//        return "OK";
//    }

    //springboot方式把原始方式简化，简单参数：参数名与形参变量名相同，定义形参即可接收参数。
    //@RequestMapping("/simpleParam")
    //public String simpleParam(String name, Integer age){
    //    System.out.println(name+ ":" + age);
    //    return "OK";
    //}



    /*
    简单参数：如果方法形参名称与请求参数名称不匹配，可以使用 @RequestParam 完成映射。
    @RequestParam中的required属性默认为true，代表该请求参数必须传递，如果不传递将报错。 如果该参数是可选的，可以将required属性设置为false。
    * 为什么要这样子改，弹幕：
    * 在项目开发的时候你就知道意义了，你直接改参数名，还得联系和你对接的前端们，否则你自己改参数名，逻辑一堆可能报错
    * 用别名的方式修正，妙啊
    @RequestParam(name = "name", required = false):
    第一个name的意思是调用了一个抽象方法，把后面的请求参数赋值给了第一个name
    * */
    @RequestMapping("/simpleParam")
    public String simpleParam(@RequestParam(name = "name", required = false) String username, Integer age){
        System.out.println(username+ ":" + age);
        return "OK";
    }



    /*
    简单参数接收的时候，前端传递了多少个请求参数，就需要方法当中声明多少个形参来接收。如果前端传递十个二十个参数呢？
    那此时通过简单参数这种接收方式就比较繁琐，后期也不便维护
    此时就可以考虑把所有的请求参数都封装到一个实体类中。但是想要成功的封装，需要满足请求的参数名和实体类的属性名字一致。
    实体参数：
    简单实体对象：请求参数名与形参对象属性名相同，定义POJO接收即可
    这个user对象中就会封装接收前端传递过来的请求参数name和age，还有另一个实体类变量address

    复杂实体对象：请求参数名与形参对象属性名相同，按照对象层次结构关系即可接收嵌套POJO属性参数。


    弹幕：
    这里和Spring注入一样,都是调用无参构造然后去set赋值
    不要给address类添加构造方法 否者会报错
    不是不要添加构造方法，而是必须要有无参构造，由于SpringMVC在实例化POJO对象时只能反射调用无参构造
     */
    //2. 实体参数
    @RequestMapping("/simplePojo")      //该注解指定请求路径
    public String simplePojo(User user){
        System.out.println(user);
        return "OK";
    }

    @RequestMapping("/complexPojo")
    public String complexPojo(User user){
        System.out.println(user);
        return "OK";
    }

    //3. 数组集合参数     数组参数：请求参数名与形参数组名称相同且请求参数为多个，定义数组类型形参即可接收参数
    @RequestMapping("/arrayParam")
    public String arrayParam(String[] hobby){
        System.out.println(Arrays.toString(hobby));
        return "OK";
    }

/*    集合参数：请求参数名与形参集合名称相同且请求参数为多个，@RequestParam 绑定参数关系。形参里面集合的变量名和前端的变量名保持一致
     因为默认情况下，前端传递多个值过来是会封装到数组里面的，如果要封装到list结合，需要加上@RequestParam注解

     弹幕：
     因为数组是一个基本的数据结构，Spring框架可以直接识别数组类型，然而Spring框架不能识别集合，如果不使用注解的话，
     框架会直接将这个集合单纯的视为一个List对象，不能从这个对象中获取内容
     */
    @RequestMapping("/listParam")
    public String listParam(@RequestParam List<String> hobby){
        System.out.println(hobby);
        return "OK";
    }

/*
    4. 日期时间参数     日期参数：使用 @DateTimeFormat 注解完成日期参数格式转换，并指定前端传递的日期参数的格式。
    弹幕：updateTime是LocalDateTime对象,println会带一个'T'分隔符
    在日期格式中，MM代表月份，而mm代表分钟。由于M和m都代表不同的含义，为了避免混淆，通常会将月份的表示大写为MM，将分钟的表示小写为mm。
    根据ISO 8601规范，日期和时间之间应该用大写字母"T"分隔。
    */
    @RequestMapping("/dateParam")
    public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
        System.out.println(updateTime);
        return "OK";
    }

/*
    5. json参数     JSON参数：JSON数据键名与形参对象属性名相同，定义POJO类型形参即可接收参数，需要使用 @RequestBody 标识
    在postman中如何发起请求来传递JSON格式的请求参数：
    json格式的数据是需要放在请求体中携带到服务端的，所以要用post请求，要设置请求体，还需要用post请求里面的body，然后选中raw格式
    raw:生的，未处理的。然后选择JSON格式

    在服务端又如何接收JSON格式的数据：
    接收JSON格式的数据，一般会通过实体对象来接受，要求JSON格式数据的键名也就是key和我们形参对象的属性名一致
    @RequestBody注解可以将JSON格式的请求数据封装到实体对象中
    */
    @RequestMapping("/jsonParam")
    public String jsonParam(@RequestBody User user){
        System.out.println(user);
        return "OK";
    }

/*
    6. 路径参数       路径参数：通过请求URL直接传递参数，使用{…}来标识该路径参数，需要使用 @PathVariable 获取路径参数
    这样写：/{id}代表的是这一块不是一个固定值，而是一个路径参数。这个路径参数id要绑定给我们的方法形参id，那这样这个方法形参
    就会自动获取到路径参数这个id值，路径参数的id需要与形参名称id保持一致
    弹幕：RESTful规范中不是提到了，路径参数用于对指定资源进行更新、删除操作
    在实际项目中 路径都是这么来的 在公司里面做实际项目用处超级大
    */
    @RequestMapping("/path/{id}")
    public String pathParam(@PathVariable Integer id){
        System.out.println(id);
        return "OK";
    }

    //传递多个路径参数：
    @RequestMapping("/path/{id}/{name}")
    public String pathParam2(@PathVariable Integer id , @PathVariable String name){
        System.out.println(id);
        System.out.println(name);
        return "OK";
    }
}
